﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Eventing.Reader;
using System.Linq;
using System.Text;
using LogManager.Base;

namespace LogManager.Windows
{
    /// <summary>
    /// Service that has a return type defined
    /// </summary>
    public interface ITypedEventService : IFilterableEventService
    {
        IEnumerable<IEvent> All();
        IEvent Get(string id);
        IQueryable<IEvent> Query();
    }

    /// <summary>
    /// Filtered service
    /// </summary>
    public interface IFilteredEventService : IFilterableEventService
    {
        IEnumerable<IEvent> All();
        IQueryable<IEvent> Query();
    }

    /// <summary>
    /// Normal interface
    /// </summary>
    public interface IEventService
    {
        ITypedEventService WithType(string typeId);
        ITypedEventService WithAnonymousType();
        IQueryable<IEvent> Query();
        IEnumerable<IEvent> All();
    }

    /// <summary>
    /// An event service that can be filtered
    /// </summary>
    public interface IFilterableEventService
    {
        IFilteredEventService InRange(DateTime? start, DateTime? end);
        IFilteredEventService WhereLevel(string level);
        IFilteredEventService WhereUser(string user);
    }

    public class WindowsEventFluentService : IEventService, ITypedEventService, IFilteredEventService
    {
        private WindowsEventQueryHelper _queryBuilder;
        private readonly WindowsLog _log;
        private string _typeId;
        private bool _includeSpecificInformation;

        public WindowsEventFluentService(WindowsLog log)
        {
            _queryBuilder = new WindowsEventQueryHelper();
            _log = log;
        }

        #region Selection (members of IEventService,IFilteredEventService,ITypedEventService)

        public IQueryable<IEvent> Query()
        {
            return ReadEvents(_log, _queryBuilder.CreateQuery()).AsQueryable();
        }

        public IEnumerable<IEvent> All()
        {
            return ReadEvents(_log, _queryBuilder.CreateQuery());
        }

        #endregion

        #region IEventService members

        ITypedEventService IEventService.WithType(string typeId)
        {
            //Set the type id
            _typeId = typeId;
            //Build condition for the query builder
            _queryBuilder.Where(WindowsEventQueryHelper.EventTypeCondition(typeId));

            return this;
        }


        ITypedEventService IEventService.WithAnonymousType()
        {
            //Add no query
            return this;
        }

        #endregion

        #region ITypedEventService members
       
        public IEvent Get(string id)
        {
            return ReadEvents(_log, _queryBuilder.CreateQuery()).Where(e => e.Id == id).SingleOrDefault();
        }

        #endregion

        #region IFilterableEventService members

        IFilteredEventService IFilterableEventService.InRange(DateTime? start, DateTime? end)
        {
            //Add condition for time range
            if(start.HasValue && end.HasValue)
                _queryBuilder.Where(WindowsEventQueryHelper.TimeRangeCondition(start.Value,end.Value));
            return this;
        }

        IFilteredEventService IFilterableEventService.WhereLevel(string level)
        {
            //Add condition for level
            if (!string.IsNullOrEmpty(level))
                _queryBuilder.Where(WindowsEventQueryHelper.LevelCondition(WindowsEventLevelHelper.GetLevel(level)));
            return this;
        }

        IFilteredEventService IFilterableEventService.WhereUser(string user)
        {
            //Add condition for user
            if(!String.IsNullOrEmpty(user))
                _queryBuilder.Where(WindowsEventQueryHelper.UserCondition(user));
            return this;
        }

        #endregion


        //Reader
        /// <summary>
        /// Return a list of events of a log. todo: Workaround, it's suboptimal to have the whole list in RAM.
        /// </summary>
        /// <param name="log">The log that contains the events</param>
        /// <param name="queryText">The query text for the log</param>
        /// <returns></returns>
        private IEnumerable<IEvent> ReadEvents(WindowsLog log, string queryText = "*")
        {
            //Init session
            var query = new EventLogQuery(log.LogName, PathType.LogName, queryText)
            {
                Session = new EventLogSession()
            };

            //Excecute reader
            using (var reader = new EventLogReader(query))
            {
                EventRecord record;

                //Return mapped event
                while ((record = reader.ReadEvent()) != null)
                {
                    var @event = _log.CreateEvent(record.Id.ToString());
                    yield return WindowsEventMapper.MapEvent(@event, record, _includeSpecificInformation);
                }
            }
        }
    }
}
